home *** CD-ROM | disk | FTP | other *** search
/ BCI NET / BCI NET Dec 94.iso / archives / programming / c / metre.lha / scan.l < prev    next >
Text File  |  1994-09-07  |  28KB  |  881 lines

  1. D                       [0-9]
  2. L                       [a-zA-Z_$]
  3. H                       [a-fA-F0-9]
  4. E                       [Ee][+-]?{D}+
  5. FS                      (f|F|l|L)
  6. IS                      (u|U|l|L)*
  7. LWSC                    [ \t\v\f]
  8. %{
  9. /*************************************************************
  10.    Copyright (c) 1993,1994 by Paul Long  All rights reserved.
  11. **************************************************************/
  12.  
  13. /*************************************************************
  14.    scan.l - This source file contains the lex specification
  15.             for Metre's Standard C lexer.  It also contains
  16.             lexical functions that can be called from the
  17.             rules() function and replacement functions for
  18.             lex's yywrap() and MKS lex's yygetc().
  19. **************************************************************/
  20.  
  21.  
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include "ytab.h"
  25. #include "metreint.h"
  26.  
  27.  
  28. /*
  29.    Decide which lex is being used based on whether YY_INIT and YY_INPUT are
  30.    defined.  It is my belief that the 4 combinations of whether these two
  31.    manifest constants are defined coincidentally indicates which lex is being
  32.    used.  I know that this method works with MKS and AT&T lex; from reading
  33.    John Levine's book, "lex & yacc," I also believe that it works with flex
  34.    and pclex.  Note: Berkeley is considered same as AT&T lex, and Posix is not
  35.    considered at all.
  36. */
  37. #ifdef YY_INIT
  38. #ifdef YY_INPUT
  39. #define MTR_PCLEX
  40. #else
  41. #define MTR_MKSLEX
  42. #endif
  43. #else
  44. #ifdef YY_INPUT
  45. #define MTR_FLEX
  46. #else
  47. #define MTR_ATTLEX
  48. #endif
  49. #endif
  50.  
  51. /*
  52.    Redefine size of miniscule yytext[].  Should have no affect on other lex's.
  53. */
  54. #define MTR_YYTEXT_SIZE 500
  55. /*
  56.    Redefine for MKS and AT&T lex.  I don't explicitly test for MTR_MKSLEX or
  57.    MTR_ATTLEX because YYLMAX should only be defined for them.
  58. */
  59. #ifdef YYLMAX
  60. #if YYLMAX < MTR_YYTEXT_SIZE
  61. #undef YYLMAX
  62. #define YYLMAX MTR_YYTEXT_SIZE
  63. #endif
  64. #endif
  65. /*
  66.    Redefine for pclex.  I don't explicitly test for MTR_PCLEX because F_BUFSIZ
  67.    should only be defined for it.
  68. */
  69. #ifdef F_BUFSIZ
  70. #if F_BUFSIZ < MTR_YYTEXT_SIZE
  71. #undef F_BUFSIZ
  72. #define F_BUFSIZ MTR_YYTEXT_SIZE
  73. #endif
  74. #endif
  75.  
  76. /*
  77.    Prior to version 2.4, flex defined a yywrap() macro.  Undefine it just in
  78.    case, because I define a yywrap() function.  This shouldn't affect the other
  79.    lex's.
  80. */
  81. #ifdef yywrap
  82. #undef yywrap
  83. #endif
  84.  
  85.  
  86. #if READ_LINE
  87.  
  88. /*
  89.    I provide a function to replace MKS' yygetc() macro, so undefine the macro.
  90.    Should have no affect on other lex's.  That's why I don't explicitly test
  91.    for MTR_MKSLEX.
  92. */
  93. #ifdef yygetc
  94. #undef yygetc
  95. #endif
  96.  
  97. /*
  98.    AT&T lex uses stdio.h's getc() to read in characters in its input() macro.
  99.    Assuming that getc() is a macro in stdio.h, redefine it to call my
  100.    yygetc() macro.
  101. */
  102. #ifdef MTR_ATTLEX
  103. #ifdef getc
  104. #undef getc
  105. #endif
  106. #define getc(x)   yygetc()
  107. #endif
  108.  
  109. /*
  110.    The following directives hopefully make Metre compatible with flex and
  111.    pclex.  I don't have either, so I can't test this.  From reading John
  112.    Levine's book, "lex & yacc," flex/pclex expects the YY_INPUT() macro to
  113.    read a block of data.  If flex/pclex is used, it will use my definition.
  114.    If flex/pclex is not used, MKS and AT&T lex will use it indirectly because
  115.    yygetc() and getc(), respectively, also use the macro.
  116.  
  117.    This diagram shows the dependencies and how Metre achieves compatibility
  118.    with the three lex's.
  119.  
  120.       getc()                     <-- AT&T lex uses
  121.          yygetc()                <-- MKS lex uses
  122.             YY_INPUT()           <-- flex/pclex uses
  123.                my_yyinput()
  124. */
  125. #ifdef YY_INPUT
  126. #undef YY_INPUT
  127. #endif
  128. #define YY_INPUT(b, r, ms) (r = my_yyinput(b, ms))
  129.  
  130. /* Size of input buffer.  Must be as large as the largest expected line. */
  131. #define INPUT_LINE_MAX_LEN    2048
  132.  
  133. #endif      /* #if READ_LINE */
  134.  
  135.  
  136. /* Define how to restart lexer based on which lex is being used. */
  137. #if defined(MTR_MKSLEX) || defined(MTR_PCLEX)
  138. #define MTR_YY_INIT  YY_INIT
  139. #elif defined(MTR_FLEX)
  140. #define MTR_YY_INIT  yyrestart(yyin)
  141. #elif defined(MTR_ATTLEX)
  142. #define MTR_YY_INIT  yy_init()
  143. #else
  144. #error Unsupported version of lex
  145. #endif
  146.  
  147.  
  148. /* External variables. */
  149.  
  150. /*
  151.    Whether to interleave the input with the output.  Set according to the
  152.    copy-input option character from the command line.
  153. */
  154. BOOLEAN display_input;
  155.  
  156. #if defined(MTR_MKSLEX) || defined(MTR_ATTLEX)
  157. /* Do nothing--the lex takes care of it. */
  158. #define INCR_YYLINENO
  159. #else
  160. /*
  161.    I know that MKS and AT&T lex support yylineno.  Don't know about the
  162.    others.  Here's one for them.  I use the technique described in
  163.    John Levine's book, "lex & yacc," of simply incrementing a line counter
  164.    whenever a newline is encountered in the input stream.  However, this is not
  165.    as accurate as how MKS and AT&T lex do it.  They increment the line counter
  166.    when the input() macro encounters a newline and decrement it when it is
  167.    pushed back via the unput() macro.  This overcomes the problem of
  168.    incrementing the line counter prematurely during look-ahead.  I took the
  169.    easy way out for lex's other than MKS or AT&T--I didn't want to provide my
  170.    own input() and output() macros for them.  You could modify them, though.
  171. */
  172. int yylineno;
  173. #define INCR_YYLINENO   (++yylineno)
  174. #endif
  175.  
  176.  
  177. /* Function prototypes for static functions. */
  178. #if READ_LINE
  179. static int yygetc(void);
  180. static int my_yyinput(char *, int);
  181. #endif
  182. static void count(void);
  183. static void comment(void);
  184. static void fire_keyword(void);
  185. static void fire_identifier(void);
  186. static void found_nonstandard(void);
  187. static BOOLEAN identifier_defined(char *);
  188. static int check_type(void);
  189. static unsigned extract_line_number(char *);
  190. static char *extract_file_name(char *);
  191. #ifdef MTR_ATTLEX
  192. static void yy_init(void);
  193. #endif
  194.  
  195. /* Static variables. */
  196.  
  197. #if READ_LINE
  198. /*
  199.    I read the input line into here then feed the lexer one character at a time
  200.    from that.  This is so that I have the entire line available in case I
  201.    need to print the line along with an error message.
  202. */
  203. static char input_line[INPUT_LINE_MAX_LEN];
  204. #endif
  205.  
  206. /* An input line is one of these three types. */
  207. static enum { BLANK_LINE, COMMENT_LINE, CODE_LINE } line_type = BLANK_LINE;
  208.  
  209. /* Whether a tab or space character was found at the beginning of a line. */
  210. static BOOLEAN found_tab;
  211. static BOOLEAN found_space;
  212.  
  213. /*
  214.    Pointer to current keyword or identifier as passed to rules if such a token
  215.    is encountered.
  216. */
  217. static char *current_keyword;
  218. static char *current_identifier;
  219. %}
  220.  
  221. %%
  222. "/*"                    { comment();   /* Read in rest of comment. */ }
  223. ^{LWSC}*#[ \t]*("line"[ \t]+)?{D}+([ \t]+\"[^"\n]*\")?.*  {
  224.                            char *temp_file_name;
  225.  
  226.                            count();
  227.  
  228.                            /* Don't know why had to subtract 1. Oh well. */
  229.                            yylineno = extract_line_number(yytext) - 1;
  230.  
  231.                            /* Use new file name if present. */
  232.                            temp_file_name = extract_file_name(yytext);
  233.                            if (temp_file_name != NULL)
  234.                               input_file_orig_name = temp_file_name;
  235.                         }
  236.  
  237. "auto"                  {  /*
  238.                               For this and the following keywords, do
  239.                               some lexical accounting, fire the keyword
  240.                               trigger in case a rule uses a keyword as a
  241.                               trigger, then return token to parser.
  242.                            */
  243.                            count(); fire_keyword(); return(TK_AUTO); }
  244. "break"                 { count(); fire_keyword(); return(TK_BREAK); }
  245. "case"                  { count(); fire_keyword(); return(TK_CASE); }
  246. "char"                  { count(); fire_keyword(); return(TK_CHAR); }
  247. "const"                 { count(); fire_keyword(); return(TK_CONST); }
  248. "continue"              { count(); fire_keyword(); return(TK_CONTINUE); }
  249. "default"               { count(); fire_keyword(); return(TK_DEFAULT); }
  250. "do"                    { count(); fire_keyword(); return(TK_DO); }
  251. "double"                { count(); fire_keyword(); return(TK_DOUBLE); }
  252. "else"                  { count(); fire_keyword(); return(TK_ELSE); }
  253. "enum"                  { count(); fire_keyword(); return(TK_ENUM); }
  254. "extern"                { count(); fire_keyword(); return(TK_EXTERN); }
  255. "float"                 { count(); fire_keyword(); return(TK_FLOAT); }
  256. "for"                   { count(); fire_keyword(); return(TK_FOR); }
  257. "goto"                  { count(); fire_keyword(); return(TK_GOTO); }
  258. "if"                    { count(); fire_keyword(); return(TK_IF); }
  259. "int"                   { count(); fire_keyword(); return(TK_INT); }
  260. "long"                  { count(); fire_keyword(); return(TK_LONG); }
  261. "register"              { count(); fire_keyword(); return(TK_REGISTER); }
  262. "return"                { count(); fire_keyword(); return(TK_RETURN); }
  263. "short"                 { count(); fire_keyword(); return(TK_SHORT); }
  264. "signed"                { count(); fire_keyword(); return(TK_SIGNED); }
  265. "sizeof"                { count(); fire_keyword(); return(TK_SIZEOF); }
  266. "static"                { count(); fire_keyword(); return(TK_STATIC); }
  267. "struct"                { count(); fire_keyword(); return(TK_STRUCT); }
  268. "switch"                { count(); fire_keyword(); return(TK_SWITCH); }
  269. "typedef"               { count(); fire_keyword(); return(TK_TYPEDEF); }
  270. "union"                 { count(); fire_keyword(); return(TK_UNION); }
  271. "unsigned"              { count(); fire_keyword(); return(TK_UNSIGNED); }
  272. "void"                  { count(); fire_keyword(); return(TK_VOID); }
  273. "volatile"              { count(); fire_keyword(); return(TK_VOLATILE); }
  274. "while"                 { count(); fire_keyword(); return(TK_WHILE); }
  275.  
  276. {L}({L}|{D})*           {
  277.                            /*
  278.                               If a replacement was provided on the
  279.                               command line for this identifier, rescan
  280.                               input which will now have the replacement
  281.                               characters.  Otherwise, do some lexical
  282.                               accounting, fire the identifier trigger in
  283.                               case a rule uses an identifier in a
  284.                               trigger, then return token to parser (this
  285.                               is either an identifier or a typedef type
  286.                               name).
  287.                            */
  288.                            if (!identifier_defined(yytext))
  289.                            {
  290.                               count();
  291.                               fire_identifier();
  292.                               return(check_type());
  293.                            }
  294.                         }
  295.  
  296. 0[xX]{H}+{IS}?          {  /*
  297.                               For this and the following constants and
  298.                               string literals, do some lexical
  299.                               accounting and return token to parser.
  300.                            */
  301.                            count(); return(TK_CONSTANT); }
  302. 0[xX]{H}+{IS}?          { count(); return(TK_CONSTANT); }
  303. 0{D}+{IS}?              { count(); return(TK_CONSTANT); }
  304. 0{D}+{IS}?              { count(); return(TK_CONSTANT); }
  305. {D}+{IS}?               { count(); return(TK_CONSTANT); }
  306. {D}+{IS}?               { count(); return(TK_CONSTANT); }
  307. '(\\.|[^\\'])+'         { count(); return(TK_CONSTANT); }
  308. {D}+{E}{FS}?            { count(); return(TK_CONSTANT); }
  309. {D}*"."{D}+({E})?{FS}?  { count(); return(TK_CONSTANT); }
  310. {D}+"."{D}*({E})?{FS}?  { count(); return(TK_CONSTANT); }
  311. \"(\\.|[^\\"])*\"       { count(); return(TK_STRING_LITERAL); }
  312.  
  313. "\.\.\."                {  /*
  314.                               For this and the following operators, do
  315.                               some lexical accounting and return token
  316.                               to parser.
  317.                            */
  318.                            count(); return(TK_ELIPSIS); }
  319. ">>="                   { count(); return(TK_RIGHT_ASSIGN); }
  320. "<<="                   { count(); return(TK_LEFT_ASSIGN); }
  321. "+="                    { count(); return(TK_ADD_ASSIGN); }
  322. "-="                    { count(); return(TK_SUB_ASSIGN); }
  323. "*="                    { count(); return(TK_MUL_ASSIGN); }
  324. "/="                    { count(); return(TK_DIV_ASSIGN); }
  325. "%="                    { count(); return(TK_MOD_ASSIGN); }
  326. "&="                    { count(); return(TK_AND_ASSIGN); }
  327. "^="                    { count(); return(TK_XOR_ASSIGN); }
  328. "|="                    { count(); return(TK_OR_ASSIGN); }
  329. ">>"                    { count(); return(TK_RIGHT_OP); }
  330. "<<"                    { count(); return(TK_LEFT_OP); }
  331. "++"                    { count(); return(TK_INC_OP); }
  332. "--"                    { count(); return(TK_DEC_OP); }
  333. "->"                    { count(); return(TK_PTR_OP); }
  334. "&&"                    { count(); return(TK_AND_OP); }
  335. "||"                    { count(); return(TK_OR_OP); }
  336. "<="                    { count(); return(TK_LE_OP); }
  337. ">="                    { count(); return(TK_GE_OP); }
  338. "=="                    { count(); return(TK_EQ_OP); }
  339. "!="                    { count(); return(TK_NE_OP); }
  340. ";"                     { count(); return(';'); }
  341. "{"                     { count(); return('{'); }
  342. "}"                     { count(); return('}'); }
  343. ","                     { count(); return(','); }
  344. ":"                     { count(); return(':'); }
  345. "="                     { count(); return('='); }
  346. "("                     { count(); return('('); }
  347. ")"                     { count(); return(')'); }
  348. "["                     { count(); return('['); }
  349. "]"                     { count(); return(']'); }
  350. "."                     { count(); return('.'); }
  351. "&"                     { count(); return('&'); }
  352. "!"                     { count(); return('!'); }
  353. "~"                     { count(); return('~'); }
  354. "-"                     { count(); return('-'); }
  355. "+"                     { count(); return('+'); }
  356. "*"                     { count(); return('*'); }
  357. "/"                     { count(); return('/'); }
  358. "%"                     { count(); return('%'); }
  359. "<"                     { count(); return('<'); }
  360. ">"                     { count(); return('>'); }
  361. "^"                     { count(); return('^'); }
  362. "|"                     { count(); return('|'); }
  363. "?"                     { count(); return('?'); }
  364.  
  365. {LWSC}                  {  /* Absorb whitespace character. */
  366.                            count(); }
  367. \n                      { INCR_YYLINENO; count(); }
  368.  
  369. ^{LWSC}*#.*             {  /* Ignore preprocessor directives. */
  370.                            count(); }
  371.  
  372. .                       {  /* Trap any non-standard characters. */
  373.                            count(); found_nonstandard(); }
  374.  
  375. %%
  376.  
  377. /*
  378.    If a replacement string was specified on command line, substitute for
  379.    this lexeme.  Return whether this identifier had a replacement string.
  380. */
  381. static BOOLEAN identifier_defined(char *id)
  382. {
  383.    unsigned i;
  384.  
  385.    /* Look through command-line arguments for the define option character. */
  386.    for (i = 1; i < cmd_line_argc; ++i)
  387.       if (strchr(OPT_INTRO_CHARS, cmd_line_argv[i][0]) != NULL &&
  388.             toupper(cmd_line_argv[i][1]) == DEFINE_OPT_CHAR)
  389.       {
  390.          char *repl_str;
  391.  
  392.          /* Look for equal sign after identifier. */
  393.          repl_str = (char *)strchr(&cmd_line_argv[i][2], '=');
  394.  
  395.          /*
  396.             If equal sign found and this is a define for this
  397.             identifier, substitute replacement string for this lexeme.
  398.          */
  399.          if (repl_str != NULL && strncmp(&cmd_line_argv[i][2], id,
  400.                                        repl_str - &cmd_line_argv[i][2]) == 0)
  401.          {
  402.             unsigned len;
  403.             char *p;
  404.  
  405.             /*
  406.                unput replacement string so that lex will scan it in as
  407.                if it occurred in the input stream instead of the
  408.                original identifier.  NOTE: If empty replacement string,
  409.                the affect is that the identifier is ignored.
  410.             */
  411.             for (len = strlen(&repl_str[1]), p = &repl_str[len]; len > 0;
  412.                                                                      --len, --p)
  413.                unput(*p);
  414.  
  415.             /*
  416.                Leave outer loop because define option character found
  417.                and processed.
  418.             */
  419.             break;
  420.          }
  421.       }
  422.  
  423.    return i < cmd_line_argc;
  424. }
  425.  
  426. /* Initialize lexer. */
  427. void init_lex(void)
  428. {
  429.    /*
  430.       Restart lex itself.  Note: I don't believe that this is absolutely
  431.       necessary for this lexer.  The lexer is not left in an unusual state
  432.       after each file, e.g., characters left in the push-back buffer or the
  433.       lexer being in a state other than INITIAL.  It is explicitly restarted
  434.       here just because "it's the right thing to do."  If this macro reference
  435.       expands to something that is not compatible with your lexer, although I
  436.       tried to make it portable, just remove it.
  437.    */
  438.    MTR_YY_INIT;
  439.  
  440.    /* Reset line_type for first line.  Start off assuming blank line. */
  441.    line_type = BLANK_LINE;
  442.  
  443.    yylineno = 1;
  444.    found_tab = FALSE;
  445.    found_space = FALSE;
  446.    current_keyword = "";
  447.    current_identifier = "";
  448. }
  449.  
  450. #ifdef MTR_ATTLEX
  451. /* Function that restarts AT&T lexers. */
  452. static void yy_init(void)
  453. {
  454.    extern int yyprevious;
  455.  
  456.    NLSTATE;
  457.    yysptr = yysbuf;
  458.    BEGIN INITIAL;
  459.  
  460. /* I don't think these absolutely need to be reset. */
  461. #if 0
  462.    extern int *yyfnd;
  463.    yyleng = 0;
  464.    yytchar = 0;
  465.    yymorfg = 0;
  466.    yyestate = 0;
  467.    yyfnd = 0;
  468. #endif
  469. }
  470. #endif
  471.  
  472. /* Fire the keyword trigger. */
  473. static void fire_keyword(void)
  474. {
  475.    current_keyword = yytext;
  476.    rules();
  477.    current_keyword = "";
  478. }
  479.  
  480. /* Fire the identifier trigger. */
  481. static void fire_identifier(void)
  482. {
  483.    current_identifier = yytext;
  484.    rules();
  485.    current_identifier = "";
  486. }
  487.  
  488.  
  489. #if READ_LINE
  490.  
  491. /* Pointer to next character in input_line[]. */
  492. static char *next_char_p;
  493.  
  494. /*
  495.    Replacement for the out-of-the-box yygetc().  This function provides
  496.    access to the entire input line, even the characters that have not
  497.    yet been scanned in.
  498. */
  499. static int yygetc(void)
  500. {
  501.    static char last_char = EOF;   /* Force subsequent getting of first line.*/
  502.    char next_char;
  503.    int characters_read;
  504.  
  505.    switch (last_char)
  506.    {
  507.    case '\n':        /* Time to get another line of input? */
  508.    case EOF:
  509.       YY_INPUT(input_line, characters_read, INPUT_LINE_MAX_LEN);
  510.       if (characters_read == 0)
  511.       {
  512.          next_char = EOF;           /* Indicate that couldn't get another line*/
  513.          next_char_p = input_line;  /* Set to something. */
  514.       }
  515.       else
  516.       {
  517.          next_char_p = input_line;
  518.          next_char = *next_char_p++;   /* Get first character from input line. */
  519.       }
  520.       break;
  521.  
  522.    default:                         /* Get next character from input line. */
  523.       next_char = *next_char_p++;
  524.    }
  525.  
  526.    last_char = next_char;
  527.  
  528.    return next_char;
  529. }
  530.  
  531. /* Read next line from input file, returning number of characters read. */
  532. static int my_yyinput(char *buf, int max_size)
  533. {
  534.    int characters_read;
  535.  
  536.    if (fgets(buf, max_size, yyin) == NULL)
  537.       buf[0] = '\0';
  538.    else
  539.       /* This is where the input line is printed if display_print is TRUE. */
  540.       if (display_input)
  541.          fputs(buf, out_fp);
  542.  
  543.    return strlen(buf);
  544. }
  545.  
  546. #endif      /* #if READ_LINE */
  547.  
  548. /*
  549.    Called by yacc at the end of a source file.  If there are more files to
  550.    process, open them and continue, else stop.
  551. */
  552. yywrap()
  553. {
  554.    int ret_val;
  555.  
  556.    /* Provide module information then fire the end-of-module trigger. */
  557.    int_mod.decisions = mod_decisions;
  558.    int_mod.functions = mod_functions;
  559.    int_mod.lines.total = yylineno - 1;
  560.    int_mod.end = TRUE;
  561.    fire_mod();
  562.    int_mod.end = FALSE;
  563.    ZERO(int_mod);
  564.  
  565.    /* See whether there is another input file to process. */
  566.    if (next_cmd_line_file < cmd_line_argc &&
  567.          (input_file = get_next_input_file(&next_cmd_line_file)) != NULL)
  568.       if (freopen(input_file, "r", yyin) != NULL)
  569.       {
  570.          /* Reinitialize yacc and lex. */
  571.          init_yacc();
  572.          init_lex();
  573.  
  574.          /*
  575.             See whether to use the original file name as provided on the
  576.             command line rather than the file name that was provided.  This
  577.             is in case the output of the preprocessor is the input file and
  578.             there are no line directives, but we'd like to use the name of
  579.             the input to the preprocessor.
  580.          */
  581.          input_file_orig_name =
  582.                get_next_input_file_orig_name(&next_cmd_line_file_orig_n);
  583.          if (input_file_orig_name == NULL)
  584.             input_file_orig_name = input_file;
  585.  
  586.          /* Fire the beginning-of-module trigger. */
  587.          ZERO(int_mod);
  588.          int_mod.begin = TRUE;
  589.          fire_mod();
  590.          int_mod.begin = FALSE;
  591.  
  592.          /* Tell yacc to continue. */
  593.          ret_val = 0;
  594.       }
  595.       else
  596.       {
  597.          warn(W_CANNOT_OPEN_FILE, input_file);
  598.  
  599.          /* Fire the end-of-project trigger. */
  600.          int_prj.end = TRUE;
  601.          fire_prj();
  602.          int_prj.end = FALSE;
  603.          ZERO(int_prj);
  604.  
  605.          /* Tell yacc to stop. */
  606.          ret_val = 1;
  607.       }
  608.    else
  609.    {
  610.       /* Fire the end-of-project trigger. */
  611.       int_prj.end = TRUE;
  612.       fire_prj();
  613.       int_prj.end = FALSE;
  614.       ZERO(int_prj);
  615.  
  616. #ifdef DEBUG_TYPEDEF
  617.       /* Used for debugging typedef processing. */
  618.       typedef_symbol_table_dump();
  619. #endif
  620.  
  621.       /* Tell yacc to stop. */
  622.       ret_val = 1;
  623.    }
  624.  
  625.    return ret_val;
  626. }
  627.  
  628. /*
  629.    The beginning of a comment has been detected.  Handle until the entire
  630.    comment has been consumed, then give control back over to lex.
  631. */
  632. static void comment(void)
  633. {
  634.    char c;
  635.  
  636.    /* If this was just a blank line, it now becomes a comment line. */
  637.    if (line_type == BLANK_LINE)
  638.       line_type = COMMENT_LINE;
  639.  
  640.    /* Loop until input exhausted or end-of-comment reached. */
  641.    for ( ; (c = input()) != '\0'; )
  642.       if (c == '*')                    /* Could be end-of-comment. */
  643.       {
  644.          char c1;
  645.  
  646.          if ((c1 = input()) == '/')
  647.             break;                     /* Is end-of-comment. */
  648.          else
  649.             unput(c1);                 /* False alarm.  Not end-of-comment. */
  650.       }
  651.       else if (c == '\n')
  652.       {
  653.          INCR_YYLINENO;
  654.  
  655.          /* Provide line information then fire the end-of-line trigger. */
  656.          int_lin.number = yylineno;
  657.          int_lin.is_comment = TRUE;
  658.          int_lin.end = TRUE;
  659.          fire_lin();
  660.          ZERO(int_lin);
  661.  
  662.          /* Reset these BOOLEANs for the next line. */
  663.          found_tab = FALSE;
  664.          found_space = FALSE;
  665.  
  666.          /* Increment the number-of-comment-lines counter. */
  667.          ++int_mod.lines.com;
  668.       }
  669. }
  670.  
  671. /*
  672.    Count various things associated with input tokens.  All input, except
  673.    for comments and preprocessor lines pass through here.
  674. */
  675. static void count(void)
  676. {
  677.    int i;
  678.  
  679.    for (i = 0; yytext[i] != '\0'; i++)
  680.       switch (yytext[i])
  681.       {
  682.       case '\n':
  683.          /* Provide line information then fire the end-of-line trigger. */
  684.          switch (line_type)
  685.          {
  686.          case BLANK_LINE:
  687.             int_lin.is_white = TRUE;
  688.             ++int_mod.lines.white;
  689.             break;
  690.  
  691.          case COMMENT_LINE:
  692.             int_lin.is_comment = TRUE;
  693.             ++int_mod.lines.com;
  694.             break;
  695.  
  696.          case CODE_LINE:
  697.             int_lin.is_exec = TRUE;
  698.             ++int_mod.lines.exec;
  699.             break;
  700.  
  701.          default:
  702.             fatal(E_LINE_TYPE);
  703.          }
  704.          /* Reset line_type for next line.  Start off assuming blank line. */
  705.          line_type = BLANK_LINE;
  706.  
  707.          int_lin.number = yylineno;
  708.          int_lin.end = TRUE;
  709.          fire_lin();
  710.          ZERO(int_lin);
  711.  
  712.          /* Reset these BOOLEANs for the next line. */
  713.          found_tab = FALSE;
  714.          found_space = FALSE;
  715.          break;
  716.  
  717.       /*
  718.          The next two cases are trying to figure out whether spaces and tabs
  719.          are both being used for indention on the same line--a little pet peeve
  720.          of mine.
  721.       */
  722.       case '\t':
  723.          if (line_type == BLANK_LINE && found_space)
  724.             int_lin.is_mixed_indent = TRUE;
  725.          found_tab = TRUE;
  726.          break;
  727.  
  728.       case ' ':
  729.          if (line_type == BLANK_LINE && found_tab)
  730.             int_lin.is_mixed_indent = TRUE;
  731.          found_space = TRUE;
  732.          break;
  733.  
  734.       default:
  735.          /*
  736.             If not one of the above, special characters, there must be code on
  737.             this line.
  738.          */
  739.          if (isgraph(yytext[i]))
  740.             line_type = CODE_LINE;
  741.       }
  742. }
  743.  
  744. /*
  745.    Return whether the token in yytext[] is just an identifier or is a
  746.    previously typedef'd name.
  747. */
  748. static int check_type(void)
  749. {
  750.    int type;
  751.  
  752.    /*
  753.       looking_for_tag is set to TRUE only when the parser is looking for a
  754.       struct, union, or enum tag.  Since tags are in a separate name space,
  755.       the current lexeme can never be a typedef type name and are therefore
  756.       always an identifier.
  757.    */
  758.    if (looking_for_tag)
  759.       type = TK_IDENTIFIER;
  760.    else
  761.       /*
  762.          If lexeme was previously defined as a typedef type name, return
  763.          token for type name, else return token for identifier.  Note that
  764.          the parser puts identifiers in the typedef symbol table, not the
  765.          lexer.
  766.       */
  767.       type = typedef_symbol_table_find(yytext) ? TK_TYPE_NAME : TK_IDENTIFIER;
  768.  
  769.    return type;
  770. }
  771.  
  772. /* Return whether the specified keyword is the current keyword. */
  773. BOOLEAN keyword(char *name)
  774. {
  775.    return strcmp(current_keyword, name) == 0;
  776. }
  777.  
  778. /* Return whether the specified identifier is the current identifier. */
  779. BOOLEAN identifier(char *name)
  780. {
  781.    return strcmp(current_identifier, name) == 0;
  782. }
  783.  
  784. /* Return pointer to current input token (lexeme). */
  785. char *token(void)
  786. {
  787.    return yytext;
  788. }
  789.  
  790. /* Return pointer to input buffer which contains current line. */
  791. char *line(void)
  792. {
  793. #if READ_LINE
  794.    return input_line;
  795. #else
  796.    return "";
  797. #endif
  798. }
  799.  
  800. /*
  801.    Return string with marker character indicating current position of parser.
  802.    Note that this line always ends with a newline character.
  803. */
  804. char *marker(void)
  805. {
  806. #if READ_LINE
  807.    static char marker_str[INPUT_LINE_MAX_LEN];
  808.    char *dst_p, *src_p;
  809.  
  810.    /* Replace all graphic characters in input buffer with space character. */
  811.    for (dst_p = marker_str, src_p = input_line;
  812.          src_p < next_char_p && *src_p != '\0' &&
  813.          /* Leave room for marker character, newline, and '\0'. */
  814.          dst_p < marker_str + sizeof marker_str - 2;
  815.          ++dst_p, ++src_p)
  816.       *dst_p = isgraph(*src_p) ? ' ' : *src_p;
  817.  
  818.    if (dst_p == marker_str)
  819.       strcpy(dst_p, "\n");       /* Nothing scanned in yet, so can't mark. */
  820.    else
  821.       strcpy(&dst_p[-1], "-\n"); /* Terminate line with marker character. */
  822.  
  823.    return marker_str;
  824. #else
  825.    return "";
  826. #endif      /* #if READ_LINE */
  827. }
  828.  
  829. /* Fire the lex trigger with nonstandard set to TRUE. */
  830. static void found_nonstandard(void)
  831. {
  832.    int_lex.nonstandard = yytext[0];
  833.    fire_lex();
  834.    ZERO(int_lex);
  835. }
  836.  
  837. /* Extract and return the line number out of the #line directive. */
  838. static unsigned extract_line_number(char *string)
  839. {
  840.    return (unsigned)strtol(&string[strcspn(string, "0123456789")], NULL, 10);
  841. }
  842.  
  843. /*
  844.    Extract and return the file name out of the #line directive. If not present,
  845.    return NULL.
  846. */
  847. static char *extract_file_name(char *string)
  848. {
  849.    char *start_of_file_name;
  850.  
  851.    /* File name is enclosed in quotes. Return NULL if no first quote. */
  852.    start_of_file_name = strchr(string, '"');
  853.    if (start_of_file_name != NULL)
  854.    {
  855.       char *end_of_file_name;
  856.  
  857.       ++start_of_file_name;      /* Skip past first quote. */
  858.  
  859.       /* If no trailing quote, return NULL. */
  860.       end_of_file_name = strchr(start_of_file_name, '"');
  861.       if (end_of_file_name == NULL)
  862.          start_of_file_name = NULL;
  863.       else
  864.       {
  865.          size_t file_name_length;
  866.          static char return_buffer[MTR_YYTEXT_SIZE];
  867.  
  868.          file_name_length = end_of_file_name - start_of_file_name;
  869.  
  870.          /* Copy file name between quotes. */
  871.          strncpy(return_buffer, start_of_file_name, file_name_length);
  872.          return_buffer[file_name_length] = '\0';
  873.  
  874.          /* Buffer is static, so it's still viable after returning. */
  875.          start_of_file_name = return_buffer;
  876.       }
  877.    }
  878.  
  879.    return start_of_file_name;
  880. }
  881.